/************** O T T R   C O P Y R I G H T   N O T I C E **************
 **                                                                   **
 ** © 2013 OTTR, Inc.  All rights reserved.                           **
 **                                                                   ** 
 *************** O T T R   C O P Y R I G H T   N O T I C E *************/

package com.jonathan.javatar;

import java.nio.charset.StandardCharsets;

/**
 *
 * @author Jonathan Paz <jonathan.paz@ottr.com>
 */
class USTarHeader extends V7TarHeader {
    public static final int MAGIC_OFFSET = 257;
    public static final int MAGIC_LENGTH = 6;
    public static final int VERSION_OFFSET = 263;
    public static final int VERSION_LENGTH = 2;
    public static final int UNAME_OFFSET = 265;
    public static final int UNAME_LENGTH = 32;
    public static final int GNAME_OFFSET = 297;
    public static final int GNAME_LENGTH = 32;
    public static final int DEVMAJOR_OFFSET = 329;
    public static final int DEVMAJOR_LENGTH = 8;
    public static final int DEVMAJOR_MAXVAL = 07777777;
    public static final int DEVMINOR_OFFSET = 337;
    public static final int DEVMINOR_LENGTH = 8;
    public static final int DEVMINOR_MAXVAL = 07777777;
    public static final int PREFIX_OFFSET = 345;
    public static final int PREFIX_LENGTH = 155;

    private String magic;
    private String version;
    private String uname;
    private String gname;
    private int devmajor;
    private int devminor;
    private String prefix;

    public USTarHeader(byte[] block) {
        super(block);
        magic = new String(block, MAGIC_OFFSET, Math.min(firstNullAfter(block,MAGIC_OFFSET,MAGIC_LENGTH),MAGIC_LENGTH), StandardCharsets.US_ASCII);
        version = new String(block, VERSION_OFFSET, VERSION_LENGTH, StandardCharsets.US_ASCII);
        uname = new String(block, UNAME_OFFSET, Math.min(firstNullAfter(block,UNAME_OFFSET,UNAME_LENGTH),UNAME_LENGTH), StandardCharsets.US_ASCII);
        gname = new String(block, GNAME_OFFSET, Math.min(firstNullAfter(block,GNAME_OFFSET,GNAME_LENGTH),GNAME_LENGTH), StandardCharsets.US_ASCII);
        devmajor = processIntField(block, DEVMAJOR_OFFSET, DEVMAJOR_LENGTH);
        devminor = processIntField(block, DEVMINOR_OFFSET, DEVMINOR_LENGTH);
        prefix = new String(block, PREFIX_OFFSET, Math.min(firstNullAfter(block,PREFIX_OFFSET,PREFIX_LENGTH),PREFIX_LENGTH), StandardCharsets.US_ASCII);
    }

    /**
     * The magic string for this record. By definition, it is "ustar".
     * @return the magic string
     */
    public String getMagic() {
        return magic;
    }

    /**
     * The version string for this record. By definition, it is "00".
     * @return the version string
     */
    public String getVersion() {
        return version;
    }

    /**
     * The name of the owner of the file this record represents
     * @return the user name
     */
    public String getUname() {
        return uname;
    }

    public void setUname(String uname) throws IllegalArgumentException {
        if (uname.length() > UNAME_LENGTH) {
            throw new IllegalArgumentException("uanme is too long");
        }
        this.uname = uname;
    }

    public String getGname() {
        return gname;
    }

    public void setGname(String gname) throws IllegalArgumentException {
        if (uname.length() > UNAME_LENGTH) {
            throw new IllegalArgumentException("uanme is too long");
        }
        this.gname = gname;
    }

    public int getDevmajor() {
        return devmajor;
    }

    public void setDevmajor(int devmajor) throws IllegalArgumentException {
        if (devmajor > DEVMAJOR_MAXVAL || devmajor < 0) {
            throw new IllegalArgumentException("Invalid devmajor value");
        }
        this.devmajor = devmajor;
    }

    public int getDevminor() {
        return devminor;
    }

    public void setDevminor(int devminor) throws IllegalArgumentException {
        if (devminor > DEVMINOR_MAXVAL || devminor < 0) {
            throw new IllegalArgumentException("Invalid devminor value");
        }
        this.devminor = devminor;
    }

    /**
     * The name of the file this record represents. This method will provide the
     * full name if the prefix was populated with the {@link #setName(java.lang.String)}
     * method.
     * @return the full name of the file
     */
    @Override
    public String getName() throws IllegalArgumentException {
        if (prefix != null && !prefix.isEmpty()) {
            return prefix + '/' + super.getName();
        } else {
            return super.getName();
        }
    }

    /**
     * Sets the name of the file this record represents. This method will populate
     * the name and prefix fields of the header block, if necessary. This method
     * assumes that the name separator is the '/' character.
     * @param name the full name of this file.
     * @throws IllegalArgumentException if the file name could not be fit into
     * the combination of name and prefix, including if the name is longer than
     * 256 characters
     */
    @Override
    public void setName(String name) throws IllegalArgumentException {
        if (name.length() <= NAME_LENGTH) {
            super.setName(name);
        } else if (name.length() <= 256) {
            String[] parts = name.split("/");
            int prefixcount = parts.length;
            int prefixlength;
            do {
                prefixlength = 0;
                --prefixcount;
                for (int i = 0; i < prefixcount; i++) {
                    prefixlength += parts[i].length();
                }
            } while (prefixlength <= 155);
            if (prefixcount > 0) {
                StringBuilder prefixBuffer = new StringBuilder(parts[0]);
                StringBuilder suffixBuffer = new StringBuilder(parts[prefixcount]);
                for (int i = 1; i < prefixcount; i++) {
                    prefixBuffer.append('/').append(parts[i]);
                }
                for (int i = prefixcount; i < parts.length; i++) {
                    suffixBuffer.append('/').append(parts[i]);
                }
                prefix = prefixBuffer.toString();
                super.setName(suffixBuffer.toString());
            } else {
                throw new IllegalArgumentException("The name could not be split into name and prefix");
            }
        } else {
            throw new IllegalArgumentException("Name is too long");
        }
    }

    @Override
    byte[] prepopulateHeader() {
        byte[] header = super.prepopulateHeader();
        byte[] magicbytes = magic.getBytes(StandardCharsets.US_ASCII);
        System.arraycopy(magicbytes, 0, header, MAGIC_OFFSET, Math.min(magicbytes.length, MAGIC_LENGTH));
        byte[] versionbytes = version.getBytes(StandardCharsets.US_ASCII);
        System.arraycopy(versionbytes, 0, header, VERSION_OFFSET, Math.min(versionbytes.length, VERSION_LENGTH));
        byte[] unamebytes = uname.getBytes(StandardCharsets.US_ASCII);
        System.arraycopy(unamebytes, 0, header, UNAME_OFFSET, Math.min(unamebytes.length, UNAME_LENGTH));
        byte[] gnamebytes = gname.getBytes(StandardCharsets.US_ASCII);
        System.arraycopy(gnamebytes, 0, header, GNAME_OFFSET, Math.min(gnamebytes.length, GNAME_LENGTH));
        byte[] devmajorbytes = String.format("%011o",devmajor).getBytes(StandardCharsets.US_ASCII);
        System.arraycopy(devmajorbytes, 0, header, DEVMAJOR_OFFSET, devmajorbytes.length);
        byte[] devminorbytes = String.format("%011o",devminor).getBytes(StandardCharsets.US_ASCII);
        System.arraycopy(devminorbytes, 0, header, DEVMINOR_OFFSET, devminorbytes.length);
        byte[] prefixbytes = prefix.getBytes(StandardCharsets.US_ASCII);
        System.arraycopy(prefixbytes, 0, header, GNAME_OFFSET, Math.min(prefixbytes.length, PREFIX_LENGTH));
        return header;
    }
    
}
